﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;

namespace Assets.Scripts.LDS
{
    class SceneGenerator
    {

        //public LayoutPlanner m_layoutPlanner; // singleton, only one instance


        //private Dictionary<string, Model> m_models;

        private SceneSemGraph m_currUserSSG; //updateCurrentTSScene
        private TextSemGraph m_textSSG; //updateCurrentTextGraph


        // generate
        public RelationModelManager m_relModelManager;  // singleton, only one instance // generator
        private SemGraphMatcher m_semanticGraphMatcher;// generator
        private SceneSemGraphManager m_sceneSemGraphManager;// generator


        private Dictionary<int, int> m_matchToNewUserSsgNodeMap = new Dictionary<int, int>();

        public SceneGenerator()
        {
            m_relModelManager = RelationModelManager.Instance;

            m_sceneSemGraphManager = new SceneSemGraphManager();
            m_semanticGraphMatcher = new SemGraphMatcher(m_sceneSemGraphManager, m_relModelManager);

            //m_layoutPlanner = new LayoutPlanner(m_relModelManager, m_sceneSemGraphManager);
        }

        public RelationModelManager getRelationManager()
        {
            return m_relModelManager;
        }

        public void Clear()
        {
            m_sceneSemGraphManager.Clear();
        }

        public void updateCurrentTextGraph(TextSemGraph tsg)
        {
            m_textSSG = tsg;
        }


        // modefied
        public void updateCurrentTSScene(SceneSemGraph ssg)
        {
            //m_currScene = ts;
            m_currUserSSG = ssg;

            if (m_currUserSSG != null)
            {
                m_currUserSSG.setNodesUnAligned();
            }
        }

        public SemanticGraph prepareQuerySG()
        {
            SemanticGraph querySG = new SemanticGraph();

            if (!m_textSSG.m_isCommand)
            {
                // binding to current scene
                if (m_currUserSSG != null)
                {
                    //resetNodes(querySG);
                    m_currUserSSG.m_nodeAlignMap.Clear();

                    // enrich or modify textSSG based on current scene; clarify determiners etc.
                    adjustTextSSGWithCurrSSG(m_textSSG, m_currUserSSG);

                    // init query graph with current scene
                    querySG = new SemanticGraph(m_currUserSSG);

                    

                    // update current UserSSG with textSSG for retrieval
                    querySG.alignAndMergeWithGraph(m_textSSG);

                    // set node matching status
                    for (int i = 0; i < querySG.m_nodeNum; i++)
                    {
                        SemNode sgNode = querySG.m_nodes[i];

                        if (m_textSSG.m_nodeAlignMap.ContainsValue(i))
                        {
                            sgNode.matchingStatus = (int)SemNode.NodeMatchingStatus.ExplicitNode;
                        }
                        else
                            sgNode.matchingStatus = (int)SemNode.NodeMatchingStatus.ContextNode;
                    }
                }
                // init a new scene with the text
                else
                {
                    querySG = new SemanticGraph(m_textSSG);

                    for (int i = 0; i < querySG.m_nodeNum; i++)
                    {
                        SemNode sgNode = querySG.m_nodes[i];
                        sgNode.matchingStatus = (int)SemNode.NodeMatchingStatus.ExplicitNode;
                    }
                }
            }

            return querySG;
        }
        public void adjustTextSSGWithCurrSSG(TextSemGraph textSSG, SceneSemGraph currSSG)
        {
            // collect entity that has determiner "each"
            List<string> anchorEntityNames = new List<string>();
            List<string> anchorDeterminers = new List<string>();
            for (int i = 0; i < textSSG.m_sentence.entityCount; i++)
            {
                SelEntity currEntity = textSSG.m_sentence.m_entities[i];
                string determiner = currEntity.m_determiner;

                if (determiner == "each" || determiner == "the")
                {
                    anchorEntityNames.Add(textSSG.m_sentence.m_entities[i].nameString);
                    anchorDeterminers.Add(determiner);
                }
            }

            // count instance number of anchor obj in current scene and add missing act and relation node
            for (int i = 0; i < anchorEntityNames.Count; i++)
            {
                string objName = anchorEntityNames[i];
                int instanceCountInCurrSSG = 0;
                int instanceCountInTextSSG = 0;

                // find object node, add duplicated relation node and act obj node
                for (int j = 0; j < currSSG.m_nodeNum; j++)
                {
                    SemNode objNode = currSSG.m_nodes[j];
                    string currNodeName = objNode.nodeName;

                    if (textSSG.m_entityNameToNodeNameMap.ContainsKey(objName))
                    {
                        objName = textSSG.m_entityNameToNodeNameMap[objName];
                    }

                    if (currNodeName == objName)
                    {
                        instanceCountInCurrSSG++;
                    }
                }

                List<int> anchorObjNodeIds = new List<int>();
                for (int j = 0; j < textSSG.m_nodeNum; j++)
                {
                    SemNode objNode = textSSG.m_nodes[j];
                    string currNodeName = objNode.nodeName;

                    if (textSSG.m_entityNameToNodeNameMap.ContainsKey(objName))
                    {
                        objName = textSSG.m_entityNameToNodeNameMap[objName];
                    }

                    if (currNodeName == objName)
                    {
                        anchorObjNodeIds.Add(j);
                        instanceCountInTextSSG++;
                    }
                }

                // add instance to TextSSG
                int addInstanceNum = instanceCountInCurrSSG - instanceCountInTextSSG;
                for (int j = 0; j < addInstanceNum; j++)
                {
                    int currAnchorNodeId = anchorObjNodeIds[0];  // use the first anchor node
                    SemNode currTSGAnchorNode = textSSG.m_nodes[currAnchorNodeId];

                    textSSG.addNode(currTSGAnchorNode);
                    int newAnchorNodeId = textSSG.m_nodeNum - 1;

                    if (currTSGAnchorNode.inEdgeNodeList.Count != 0)
                    {
                        for (int r = 0; r < currTSGAnchorNode.inEdgeNodeList.Count; r++)
                        {
                            int relNodeId = currTSGAnchorNode.inEdgeNodeList[r];
                            SemNode relNode = textSSG.m_nodes[relNodeId];

                            // find determiner of the act obj; add instance if the determiner is not "a" or specific number
                            int actNodeId = relNode.activeNodeList[0];
                            SemNode actNode = textSSG.m_nodes[actNodeId];

                            string actDeterminer = textSSG.getDeterminerOfEntity(actNode.nodeName);
                            if (actDeterminer != "a" || anchorDeterminers[i] == "each")
                            {
                                textSSG.addNode(relNode);
                                int newRelNodeId = textSSG.m_nodeNum - 1;
                                textSSG.addEdge(newRelNodeId, newAnchorNodeId);

                                for (int k = 0; k < relNode.activeNodeList.Count; k++)
                                {
                                    actNodeId = relNode.activeNodeList[k];
                                    actNode = textSSG.m_nodes[actNodeId];

                                    textSSG.addNode(actNode);

                                    int newActNodeId = textSSG.m_nodeNum - 1;
                                    textSSG.addEdge(newActNodeId, newRelNodeId);
                                }
                            }
                        }
                    }
                }
            }

            textSSG.parseNodeNeighbors();
        }
        public void resetNodes(SemanticGraph sg)
        {
            sg.m_nodeAlignMap.Clear();
        }



        public List<SceneSemGraph> generateTSScenes(int targetMatchNum)
        {
            // Put TextSemGraph into QuerySG 
            SemanticGraph querySG = prepareQuerySG();

            if (querySG == null)
            {
                TextDialog.textDebug("No command could be executed");
            }

            List<SceneSemGraph> scenes = new List<SceneSemGraph> ();

            if (!querySG.m_isCommand)
            {
                m_semanticGraphMatcher.updateQuerySG(querySG);

                List<SceneSemGraph> matchedSSGs = m_semanticGraphMatcher.retrieveDatabaseSSGs(targetMatchNum);

                for (int i = 0; i < matchedSSGs.Count; i++)
                {
                    SceneSemGraph newUserSsg = accommodateToCurrTSScene(matchedSSGs[i]);
                    
                    newUserSsg.covertToTSScene();

                    //s->m_layoutPlanner = m_layoutPlanner;
                    //s.m_relModelManager = m_relModelManager;
                    scenes.Add(newUserSsg);
                }
            }
            return scenes;
        }

        // todo 
        //public void executeCommandsToCurrentScene()
        //{
        //    if (m_currUserSSG == null) return;

        //    m_currScene.m_toPlaceModelIds.clear();

        //    for (int i = 0; i < m_currUserSSG.m_nodes.Count; i++)
        //    {
        //        SemNode objNode = m_currUserSSG.m_nodes[i];

        //        if (objNode.nodeType == "object")
        //        {
        //            int metaModelId = m_currUserSSG.m_graphNodeToModelListIdMap[i];
        //            MetaModel md = m_currUserSSG.m_graphMetaScene.m_metaModellList[metaModelId];
        //            if (md.isJustReplaced)
        //            {
        //                md.isJustReplaced = false;
        //            }
        //        }
        //    }

        //    for (int i = 0; i < m_textSSG.m_nodes.Count; i++)
        //    {
        //        SemNode commandNode = m_textSSG.m_nodes[i];

        //        if (commandNode.nodeType == "command")
        //        {
        //            if (commandNode.inEdgeNodeList.Count != 0)
        //            {
        //                for (int j = 0; j < commandNode.inEdgeNodeList.Count; j++)
        //                {
        //                    int directObjNodeId = commandNode.inEdgeNodeList[j];
        //                    SemNode dirObjNode = m_textSSG.m_nodes[directObjNodeId];

        //                    // collect all direct objs
        //                    for (int j = 0; j < m_currUserSSG.m_nodes.Count; j++)
        //                    {
        //                        SemNode objNode = m_currUserSSG.m_nodes[j];
        //                        if (objNode.nodeType == "object" && objNode.nodeName == dirObjNode.nodeName)
        //                        {
        //                            m_layoutPlanner->executeCommand(m_currScene, commandNode.nodeName, j);
        //                        }
        //                    }
        //                }
        //            }
        //        }
        //    }
        //}

        // scene binding
        public SceneSemGraph accommodateToCurrTSScene(SceneSemGraph matchedSg)
        {
            SceneSemGraph newUserSsg;

            // unalign nodes of matched ssg
            matchedSg.setNodesUnAligned();

            if (m_currUserSSG == null)
            {
                newUserSsg = new SceneSemGraph(matchedSg);
                //newUserSsg.initMetaModelSuppPlanes(m_models);

                // set scene file name that current match comes from
                newUserSsg.m_graphMetaScene.m_sceneFileName = matchedSg.m_graphMetaScene.m_sceneFileName;
                return newUserSsg;
            }

            // set status for object already in current ssg
            for (int i = 0; i < m_currUserSSG.m_graphMetaScene.m_metaModellList.Count; i++)
            {
                m_currUserSSG.m_graphMetaScene.m_metaModellList[i].isAlreadyPlaced = true;
                m_currUserSSG.m_graphMetaScene.m_metaModellList[i].isBvhReady = false;  // update BVH before each evolution
            }

            // copy from current sg
            newUserSsg = new SceneSemGraph(m_currUserSSG);

            // align object node and relationship node
            double alignScore = 0;
            matchedSg.m_nodeAlignMap.Clear();
            matchedSg.alignObjectNodesToGraph(newUserSsg, ref alignScore);
            matchedSg.alignRelationNodesToGraph(newUserSsg, ref alignScore);

            // merge matched ssg to current scene ssg
            newUserSsg.mergeWithMatchedSSG(matchedSg);

            // geometry alignment
            //newUserSsg.initMetaModelSuppPlanes(m_models);
            //m_layoutPlanner->initPlaceByAlignRelation(matchedSg, newUserSsg);

            // set scene file name that current match comes from
            newUserSsg.m_graphMetaScene.m_sceneFileName = matchedSg.m_graphMetaScene.m_sceneFileName;
            return newUserSsg;
        }

    }
}
